﻿using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.IO;

namespace DesignByFarooq.PowershellByExample
{
    public class MemoryStatusFile
    {
        private static readonly string[] memoryObjects = new string[]
        {
                "Memory Manager",
                "Buffer Pool",
                "Process/System Counts",
                "Procedure Cache",
                "Global Memory Objects",
                "Memory Pool Manager"
        };

        private static readonly string[] memoryObjectAttributes = new string[]
        {
                "VM Reserved",
                "VM Committed",
                "Locked Pages Allocated",
                "Reserved Memory",
                "Reserved Memory In Use",
                "MultiPage Allocator",
                "SinglePage Allocator",
                "SM Reserved",
                "SM Committed",
                "Committed",
                "Target",
                "Database",
                "Dirty",
                "In IO",
                "Latched",
                "Free",
                "Stolen",
                "Reserved",
                "Visible",
                "Stolen Potential",
                "Limiting Factor",
                "Last OOM Factor",
                "Last OS Error",
                "Page Life Expectancy",
                "Available Physical Memory",
                "Available Virtual Memory",
                "Available Paging File",
                "Working Set",
                "Percent of Committed Memory in WS",
                "Page Faults",
                "System physical memory high",
                "System physical memory low",
                "Process physical memory low",
                "Process virtual memory low",
                "TotalProcs",
                "TotalPages",
                "InUsePages",
                "Resource",
                "Locks",
                "XDES",
                "SETLS",
                "SE Dataset Allocators",
                "SubpDesc Allocators",
                "SE SchemaManager",
                "SE Column Metadata Cache",
                "SQLCache",
                "Replication",
                "ServerGlobal",
                "XP Global",
                "SortTables",
                "Grants",
                "Waiting",
                "Available",
                "Current Max",
                "Future Max",
                "Physical Max",
                "Next Request",
                "Waiting For",
                "Cost",
                "Timeout",
                "Wait Time",
                "Overall Memory",
                "Target Memory",
                "Last Notification",
                "Early Termination Factor",
                "Configured Units",
                "Available Units",
                "Acquires",
                "Waiters",
                "Threshold Factor",
                "Threshold",
                "Reserved Current",
                "Reserved Limit",
                "Allocations",
                "Predicted",
                "Private Target",
                "Private Limit",
                "Total Target",
                "Total Limit",
                "OOM Count",
                "Rate",
                "Target Allocations",
                "Future Allocations",
                "Overall"
        };

        private static readonly RegexOptions regexOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase;
        private static readonly string memoryNodePattern = @"Memory node Id = (?<NodeId>\d+)";
        private static readonly string nodeScopedObjectPattern = @"(?<ObjectName>\w+)\s+\(node (?<NodeId>\d+)\)";
        private static readonly string objectTotalPattern = @"(?<ObjectName>\w+)\s+\(Total\)";
        private static readonly string internalObjectPattern = @".+ \(internal\)";
        private static readonly string defaultObjectPattern = @".+ \(default\)";
        private static readonly string collectionTimePattern = @"Start time:\s+(?<CollectionTime>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3})";
        private static readonly string memoryObjectAttributePattern = @"(?<AttributeName>.+)(?<AttributeValue>\d+)";

        public static Tuple<string, int> GetNodeScopedObject(
            string input
            )
        {
            if (string.IsNullOrEmpty(input))
            {
                throw new ArgumentException(
                    "Parameter cannot be null or empty.",
                    "input"
                    );
            }

            IEnumerable<MemoryStatusReaderMatch> matches = MemoryStatusFile.GetMatches(
                input,
                MemoryStatusFile.nodeScopedObjectPattern,
                new string[] { "ObjectName", "NodeId" }
                );

            if (matches.Any() == false)
            {
                return null;
            }

            MemoryStatusReaderMatch[] matchArray = matches.ToArray();

            return new Tuple<string, int>(
                    matchArray[0].Value,
                    int.Parse(matchArray[1].Value)
                    );
        }

        public static int? GetMemoryNodeId(
            string input
            )
        {
            if (string.IsNullOrEmpty(input))
            {
                throw new ArgumentException(
                    "Parameter cannot be null or empty.",
                    "input"
                    );
            }

            MemoryStatusReaderMatch match = MemoryStatusFile.GetMatches(
                input,
                MemoryStatusFile.memoryNodePattern,
                new string[] { "NodeId" }
                )
                .FirstOrDefault();

            if (match == null)
            {
                return null;
            }

            return int.Parse(
                    match.Value
                    );
        }

        public static DateTime? GetCollectionTime(
            string input
            )
        {
            if (string.IsNullOrEmpty(input))
            {
                return null;
            }

            MemoryStatusReaderMatch match = MemoryStatusFile.GetMatches(
                input,
                MemoryStatusFile.collectionTimePattern,
                new string[] { "CollectionTime" }
                )
                .FirstOrDefault();

            if (match == null)
            {
                return null;
            }

            DateTime result = DateTime.MinValue;

            bool parsed = DateTime.TryParse(
                match.Value,
                out result
                );

            if (parsed == false)
            {
                return null;
            }
            else
            {
                return result;
            }
            
        }

        public static MemoryStatusFilePartitionCollection GetPartitions(
            string inputFile
            )
        {
            if (string.IsNullOrEmpty(inputFile))
            {
                throw new ArgumentException(
                    "Parameter cannot be null or empty.",
                    "inputFile"
                    );
            }

            FileStream fileStream = new FileStream(
                inputFile,
                FileMode.Open,
                FileAccess.Read,
                FileShare.None
                );

            StreamReader reader = new StreamReader(
                fileStream
                );

            MemoryStatusFilePartitionCollection collection = new MemoryStatusFilePartitionCollection(
                inputFile
                );

            using (reader)
            {
                string line = null;
                int currentOffset = 0;
                
                while ((line = reader.ReadLine()) != null)
                {
                    int length = line.Length;

                    if (line.Length == 0)
                    {
                        currentOffset += 2;
                        continue;
                    }

                    currentOffset += length;
                    char[] buffer = new char[length + 1];

                    reader.BaseStream.Seek(
                        currentOffset - length,
                        SeekOrigin.Begin
                        );

                    reader.DiscardBufferedData();

                    reader.Read(
                        buffer,
                        0,
                        length + 1
                        );

                    string input = new string(
                        buffer
                        );

                    DateTime? dateTime = MemoryStatusFile.GetCollectionTime(
                        input
                        );

                    if (dateTime.HasValue != false)
                    {
                        int startOffset = currentOffset - length;
                        int endOffset = 0;

                        collection.Add(
                            new MemoryStatusFilePartition(startOffset, 0)
                            );
                    }
                }
            }

            return collection;
        }

        private static IEnumerable<MemoryStatusReaderMatch> GetMatches(
            string input,
            string pattern,
            string[] groupNames
            )
        {
            Match match = Regex.Match(
                input,
                pattern,
                MemoryStatusFile.regexOptions
                );

            if (match.Success != false)
            {
                foreach (string groupName in groupNames)
                {
                    yield return new MemoryStatusReaderMatch(
                        match.Groups[groupName].Value,
                        match.Index,
                        match.Length
                        );
                }
            }
        }
    }
}
